package com.ruoyi.web.utils;

import cn.hutool.core.util.NumberUtil;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * 以太坊 web3j 相关方法
 */
public class EthUtil {

    private final Web3j web3j;

    /**
     * web3j url需要初始化
     * @param web3j web3j
     */
    public EthUtil(Web3j web3j) {
        this.web3j = web3j;
    }

    /**
     * Trc20余额获取
     * @param address 查询地址
     * @param smartAddress 智能合约
     * @return r
     */
    public String getEthTrc20Balance(String address,String smartAddress) {
        try {
            // 出口参数
            List<TypeReference<?>> outputParameters = new ArrayList<>();
            TypeReference<Uint256> typeReference = new TypeReference<Uint256>() {
            };
            outputParameters.add(typeReference);
            String methodName = "balanceOf";
            // 入口参数
            List<Type> inputParameters = new ArrayList<>();
            // 用户账户地址
            Address userAddress = new Address(address);
            inputParameters.add(userAddress);
            // 获取ETH余额
            Function function = new Function(methodName, inputParameters, outputParameters);
            String data = FunctionEncoder.encode(function);
            Request<?, EthCall> ethCallRequest = web3j.ethCall(
                    Transaction.createEthCallTransaction(
                            // 地址（账户）
                            address,
                            // 智能合约地址（币种列表）
                            smartAddress,
                            data
                    ),
                    DefaultBlockParameterName.LATEST);
            EthCall send;
            try {
                send = ethCallRequest.send();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            // 保留币种列表中该币种指定的精度，转化成String类型数据
            return new BigInteger(send.getValue().substring(2), 16).toString();
        } catch (Exception e) {
            return "0";
        }
    }

    /**
     * Trc20转账交易
     * @param privateKey 私钥
     * @param fromAddress 来源地址
     * @param toAddress 到账地址
     * @param smartAddress 智能合约
     * @param balance 转账数量
     * @return r
     */
    public EthSendTransaction ethSend(String privateKey, String fromAddress, String toAddress, String smartAddress, String balance) {
        Credentials credentials = Credentials.create(privateKey);

        // 查询指定地址发生的交易数量
        EthGetTransactionCount ethGetTransactionCount;
        try {
            ethGetTransactionCount = web3j.ethGetTransactionCount(fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }

        BigInteger nonce = ethGetTransactionCount.getTransactionCount();

        // 查询当前的手续费gas价格，单位：wei。
        EthGasPrice ethGasPrice;
        try {
            ethGasPrice = web3j.ethGasPrice().send();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        BigInteger gasPrice = ethGasPrice.getGasPrice();
        // 账户USC公链余额
        BigInteger bigInteger = NumberUtil.toBigInteger(balance);

        // 封装转账交易
        Function transferFunction = new Function(
                "transfer",
                Arrays.asList(new Address(toAddress), new Uint256(bigInteger)),
                Collections.singletonList(new TypeReference<Type>() {
                }));

        String encodedFunction = FunctionEncoder.encode(transferFunction);

        // 签名交易
        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
                gasPrice,
                BigInteger.valueOf(100_000L),
                smartAddress, encodedFunction);

        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signMessage);

        // 广播交易，向为签名交易创建一个新的消息调用交易或合约。
        EthSendTransaction ethSendTransaction;
        try {
            ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
        } catch (InterruptedException | ExecutionException e) {
            throw new RuntimeException(e);
        }
        return ethSendTransaction;
    }

    /**
     * 余额类型转换
     * @param balance 金额
     * @return BigDecimal
     */
    public BigDecimal getBigDecimal(String balance) {
        return Convert.fromWei(new BigDecimal(balance), Convert.Unit.ETHER).setScale(8, RoundingMode.DOWN);
    }

}
